#include <cstdio>
#include <deque>

#include <cmemory.h>
#include <scrptrun.h>
#include <unicode/ubidi.h>

struct ScriptRecord {
    UChar32 startChar;
    UChar32 endChar;
    UScriptCode scriptCode;
};

typedef struct BidiScriptRunRecords {
    bool isRtl;
    std::deque<struct ScriptRecord> records;
} BidiScriptRunRecords;

#define EXAMPLE 0

#if EXAMPLE == 0
UChar testChars[] = {
    0x0020, 0x0946, 0x0939, 0x093F, 0x0928, 0x094D, 0x0926, 0x0940, 0x0020,
    0x0627, 0x0644, 0x0639, 0x0631, 0x0628, 0x064A, 0x0629, 0x0020,
    0x059A, 0x05B3, 0x05D2, 0x05E6, 0x05F2, 0x05DC, 0x05E9, 0x05F3,
    0x0420, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, 0x0020,
    'E', 'n', 'g', 'l', 'i', 's', 'h',  0x0020,
    0x6F22, 0x5B75, 0x3068, 0x3072, 0x3089, 0x304C, 0x306A, 0x3068,
    0x30AB, 0x30BF, 0x30AB, 0x30CA,
    0xD801, 0xDC00, 0xD801, 0xDC01, 0xD801, 0xDC02, 0xD801, 0xDC03
};
// Processing Bidi Run = 0 -- run-start = 0, run-len = 9, isRTL = 0
// Script 'Devanagari' from 0 to 9.
// Processing Bidi Run = 1 -- run-start = 9, run-len = 16, isRTL = 1
// Script 'Hebrew' from 17 to 25.
// Script 'Arabic' from 9 to 17.
// Processing Bidi Run = 2 -- run-start = 25, run-len = 36, isRTL = 0
// Script 'Cyrillic' from 25 to 33.
// Script 'Latin' from 33 to 41.
// Script 'Han' from 41 to 43.
// Script 'Hiragana' from 43 to 49.
// Script 'Katakana' from 49 to 53.
// Script 'Deseret' from 53 to 61.
#elif EXAMPLE == 1
const UChar testChars[] = u"مرحباً، العالم جاهز";
// Processing Bidi Run = 0 -- run-start = 0, run-len = 20, isRTL = 1
// Script 'Arabic' from 0 to 20.
#elif EXAMPLE == 2
const UChar testChars[] = u"،";
// Processing Bidi Run = 0 -- run-start = 0, run-len = 2, isRTL = 0
// Script 'Common' from 0 to 2.
#endif

void collectBidiScriptRuns(BidiScriptRunRecords &scriptRunRecords,
    const UChar *chars, int32_t start, int32_t end, bool isRTL) {
    scriptRunRecords.isRtl = isRTL;

    icu::ScriptRun scriptRun(chars, start, end);
    while (scriptRun.next()) {
        ScriptRecord scriptRecord;
        scriptRecord.startChar = scriptRun.getScriptStart();
        scriptRecord.endChar = scriptRun.getScriptEnd();
        scriptRecord.scriptCode = scriptRun.getScriptCode();

        scriptRunRecords.records.push_back(scriptRecord);
    }
}

int main()
{
    UBiDi* bidi = ubidi_open();
    UBiDiLevel bidiReq = UBIDI_DEFAULT_LTR;
    int stringLen = sizeof (testChars) / sizeof (testChars[0]);
    if (bidi) {
        UErrorCode status = U_ZERO_ERROR;
        ubidi_setPara(bidi, testChars, stringLen, bidiReq, NULL, &status);
        if (U_SUCCESS(status)) {
            ubidi_getParaLevel(bidi);
            ssize_t rc = ubidi_countRuns(bidi, &status);

            for (size_t i = 0; i < size_t(rc); ++i) {
                int32_t startRun = -1;
                int32_t lengthRun = -1;
                UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
                bool isRTL = (runDir == UBIDI_RTL);
                printf("Processing Bidi Run = %lu -- run-start = %d, run-len = %d, isRTL = %d\n",
                        i, startRun, lengthRun, isRTL);

                BidiScriptRunRecords scriptRunRecords;
                collectBidiScriptRuns(scriptRunRecords, testChars, startRun, lengthRun, isRTL);
                while (!scriptRunRecords.records.empty()) {
                    struct ScriptRecord scriptRecord;
                    if (scriptRunRecords.isRtl) {
                        scriptRecord = scriptRunRecords.records.back();
                        scriptRunRecords.records.pop_back();
                    } else {
                        scriptRecord = scriptRunRecords.records.front();
                        scriptRunRecords.records.pop_front();
                    }

                    int32_t     start = scriptRecord.startChar;
                    int32_t     end = scriptRecord.endChar;
                    UScriptCode code = scriptRecord.scriptCode;
                    printf("Script '%s' from %d to %d.\n", uscript_getName(code), start, end);
                }
            }
        }
        ubidi_close(bidi);
    }
}
